/*

Stickman Adventure - a game created by Daco & his cousin

Copyright (C) 2011-2012  DacoTaco & Wouterke

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation version 2.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.


*/
#include "map_file.h"
#include <stdio.h>

#ifdef DEBUG
	#define gprintf printf
#else
	#define gprintf(...)
#endif


std::vector< _map_file_entry > map_file;
int map_x = 1800;
int map_y = 1800;

char VerifyMap(const char *filename)
{
	int ret = 1;
	if(filename == NULL)
	{
		ret = -2;
		goto ret;
	}

	FILE* pMap = fopen(filename,"rb");
	if(pMap == NULL)
	{
		ret = -3;
		goto ret;
	}

	_map_file_header header;
	if ( read_file(pMap,&header,sizeof(_map_file_header)) != sizeof(_map_file_header))
	{
		ret = -4;
		goto ret;
	}

	if(header.sig != 0x44574d00) //DWM\0
	{
		ret = -5;
		goto ret;
	}

	if(header.map_x <= 0 && header.map_y <= 0)
	{
		ret = -6;
		goto ret;
	}

	//TODO : replace with realtime check that loads every entry and counts to see if its correct
	/*fseek (pMap , 0 , SEEK_END);
	int lSize = ftell (pMap);
	rewind (pMap);
	if(lSize !=  (header.entries * sizeof(_map_file_entry)) + sizeof(_map_file_header) )
		return -6;*/

	//its all fine :)
ret:
	if(pMap)
		fclose(pMap);
	return ret;
}
char ReadMap(std::string filename)
{
	if(map_file.size() > 0)
		map_file.clear();

	if(filename.c_str() == NULL)
		return -1;
	char ret = 1;
	ret = VerifyMap(filename.c_str());
	if(ret < 0)
		return ret;

	FILE* pMap = fopen(filename.c_str(),"rb");
	if(pMap == NULL)
		return -8;

	_map_file_header header;
	read_file(pMap,&header,sizeof(_map_file_header));
	map_x = header.map_x;
	map_y = header.map_y;
	
	for(unsigned int x = 0;x < header.entries;x++)
	{
		//read header & others
		gprintf("reading entry %d of %d\n\n\n",x+1,header.entries);
		_map_file_entry temp;
		memset(&temp,0,sizeof(_map_file_entry));
		int tet = read_file(pMap,&temp,MAP_SIZE_ENTRIES);
		//printf("fffffuuuu %d\n",tet);
		if ( tet != MAP_SIZE_ENTRIES)
		{
			ret = -9; //todo, check if we reached an error or EOF
			goto _return;
		}

		//read the texture name
		if(read_file(pMap,&temp.name_size,1) != 1)
		{
			ret = -10;
			goto _return;
		}
		char *temp_tex = (char*)malloc(sizeof(char)*temp.name_size);
		if(temp_tex == NULL)
		{
			ret = -11;
			goto _return;
		}
		memset(temp_tex,0,temp.name_size);
		if(temp.name_size != fread(temp_tex,1,temp.name_size,pMap))
		{
			ret = -12;
			free(temp_tex);
			temp_tex = NULL;
			goto _return;
		}
		temp.texture_name.assign(temp_tex,temp.name_size);
		free(temp_tex);
		temp_tex = NULL;

		//read description
		if(read_file(pMap,&temp.desc_size,1) != 1)
		{
			ret = -13;
			goto _return;
		}
		temp_tex = (char*)malloc(sizeof(char)*temp.desc_size);
		if(temp_tex == NULL)
		{
			ret = -14;
			goto _return;
		}
		memset(temp_tex,0,temp.desc_size);
		if(temp.desc_size != fread(temp_tex,1,temp.desc_size,pMap))
		{
			ret = -15;
			free(temp_tex);
			temp_tex = NULL;
			goto _return;
		}
		temp.entry_desc.assign(temp_tex,temp.desc_size);
		free(temp_tex);
		temp_tex = NULL;

		//read command & subcommand & type
		if(read_file(pMap,&temp.command,3) != 3)
		{
			ret = -16;
			goto _return;
		}
		else
			gprintf("command & subcommand : %x & %x & %x\n",temp.command,temp.subcommand,temp.type);

		//read the ending to verify file
		if(read_file(pMap,&temp.ending,4) != 4)
		{
			ret = -17;
			goto _return;
		}
		if(temp.ending == 0xdeadbeef)
			map_file.push_back(temp);
		else
			gprintf("deadbeef not found, found %x instead\n",temp.ending);
	}
_return:
	if(pMap)
		fclose(pMap);
	//if(ret >= 0)
		//return map_file.size();
	return ret;
}
char WriteMap(std::string filename)
{
	FILE* pMap = fopen(filename.c_str(),"wb");
	if(pMap == NULL)
		return -1;

	_map_file_header* header = new _map_file_header;
	header->map_x = map_x;
	header->map_y = map_y;
	header->entries = map_file.size();
	header->sig = 0x44574d00;
	//endian swap if needed. LE SIGH
	write_file(pMap,header,sizeof(_map_file_header));
	for(unsigned int x = 0;x < map_file.size();x++)
	{
		char temp[255];
		memset(temp,0,255);
		strncpy(temp,map_file.at(x).texture_name.c_str(),map_file.at(0).name_size);
		write_file(pMap,&map_file[x],17); //first part writen
		fwrite(temp,1,map_file.at(x).name_size,pMap); //texture name written
		write_file(pMap,&map_file.at(x).command,3); //the last part
		write_file(pMap,&map_file.at(x).ending,4);

	}	
	if(pMap)
		fclose(pMap);
	return 1;
}
size_t write_file( FILE* file, void* nbr ,int size )
{
	if(file == NULL || size == 0)
		return 0;
	if(!is_big_endian() )//&& size >= 4)
	{
		for(int x=0;x<=size;)
		{
			if(x+4 <= size)
			{
				unsigned int* pTmp = (unsigned int*)nbr;
				unsigned int temp = *(pTmp+(x/4));
				endian_swap(temp);
				//printf("%08x is now and will turn in ->%08x\n",*(pTmp+(x/4),temp);
				x+=4;
				fwrite(&temp,1,4,file); 
			}
			else if(x+2 <= size)
			{
				unsigned short* pTmp = (unsigned short*)nbr;
				//printf("%08x(0x%08x , %x) lolol\n",*(pTmp+x),pTmp+x,x);
				unsigned short temp = *(pTmp+(x/2));

				//NOTE : FOR SOME ODD REASON, fwrite swaps shorts. so ye, no need for a swap.
				//endian_swap(temp);
				//printf("%08x is now and will turn in ->%d",*(pTmp+x),temp);

				fwrite(&temp,1,2,file);
				x += 2;
			}
			else if(x+1 <= size)
			{
				unsigned char* pTmp = (unsigned char*)nbr;
				unsigned char temp = *(pTmp+x);
				//printf("writing %x, before was %x\n",temp,*(pTmp+x));
				fwrite(&temp,1,1,file);
				x++;
				if(x+1 >= size)
					break;
			}
			else
				break;
			
		}
		//fwrite(pTmp,1,size,file ); 
	}
   return 1;
}

size_t read_file( FILE* file, void* nbr ,int size )
{
	if(file == NULL || nbr == NULL || size == 0)
		return 0;
	size_t res = fread(nbr, 1, size , file );
	if(!is_big_endian() && size >= 4)
	{
		//printf("swapping content\n");
		std::vector<unsigned char> temp(size);
		memcpy(&temp[0],nbr,size);
		for(int x=0;x<size;)
		{
			if(size >= 4)
			{
				endian_swap(*(unsigned int*)&temp[x]);
				if(x+4 >= size)
					break;
				x = x+4;
			}
			else
			{
				endian_swap(*(unsigned short*)&temp[x]);
				if(x+2 >= size)
					break;
				x = x+2;
			}
			
		}
		memcpy(nbr,&temp[0],size);
	}
	return res;
}
int is_big_endian(void)
{
    union {
        uint32_t i;
        char c[4];
    } bint = {0x01020304};

    return bint.c[0] == 1; 
}
inline void endian_swap(unsigned short& x)
{
    x = (x>>8) | 
        (x<<8);
}

inline void endian_swap(unsigned int& x)
{
    x = (x>>24) | 
        ((x<<8) & 0x00FF0000) |
        ((x>>8) & 0x0000FF00) |
        (x<<24);
}
// __int64 for MSVC, "long long" for gcc
inline void endian_swap(unsigned __int64& x)
{
    x = (x>>56) | 
        ((x<<40) & 0x00FF000000000000) |
        ((x<<24) & 0x0000FF0000000000) |
        ((x<<8)  & 0x000000FF00000000) |
        ((x>>8)  & 0x00000000FF000000) |
        ((x>>24) & 0x0000000000FF0000) |
        ((x>>40) & 0x000000000000FF00) |
        (x<<56);
}
